#!/usr/bin/python
import sys
import math
import random
from PyQt4 import Qt, QtCore, QtGui
import PyQt4.Qwt5 as Qwt
from dataGen_ui import Ui_dataGenUI

class Gui(QtGui.QMainWindow):
    def __init__(self,parent=None):
        QtGui.QWidget.__init__(self,parent)
        self.ui = Ui_dataGenUI()
        self.ui.setupUi(self)
        
        self.ui.componentsTable.showGrid()       
        self.ui.componentsTable.resizeRowsToContents()
        
        # connect signals with slots
        QtCore.QObject.connect(self.ui.actionGenerate,QtCore.SIGNAL("triggered()"),self.generateData)
        QtCore.QObject.connect(self.ui.actionSaveData,QtCore.SIGNAL("triggered()"),self.saveData)
        QtCore.QObject.connect(self.ui.actionLoadParameters,QtCore.SIGNAL("triggered()"),self.loadParameters)
        QtCore.QObject.connect(self.ui.actionSaveParameters,QtCore.SIGNAL("triggered()"),self.saveParameters)
        QtCore.QObject.connect(self.ui.typeSelectBox,QtCore.SIGNAL("activated(int)"),self.comboBoxSelection)
        QtCore.QObject.connect(self.ui.addButton,QtCore.SIGNAL("clicked()"),self.addComponent)
        QtCore.QObject.connect(self.ui.deleteButton,QtCore.SIGNAL("clicked()"),self.deleteComponent)
        QtCore.QObject.connect(self.ui.clearButton,QtCore.SIGNAL("clicked()"),self.clearComponents)
        QtCore.QObject.connect(self.ui.addNoiseCheckBox,QtCore.SIGNAL("clicked()"),self.setNoise)
        QtCore.QObject.connect(self.ui.spacingFileButton,QtCore.SIGNAL("clicked()"),self.getSpacingFile)
    # __init__()
        
    def generateData(self):
        print "== generating data"
        type = self.ui.typeSelectBox.currentIndex()
        sType = self.ui.typeSelectBox.currentText()
        print "----------------------------------------------------"
        print "type:",sType
        print "----------------------------------------------------"
        
        if type == 0: #even
            self.generateEvenData()
        elif type==1: #random times
            self.generateRandomTimesData()
        elif type==2: #random drops
            self.generateRandomDropsData()
        elif type==3: #duty cycle
            self.generateDutyCycleData()
        elif type==4: #spacing file
            self.generateSpacingFileData()

    # generateData()
    
    
    def generateEvenData(self):
        print "even data"
        plot = self.ui.qwtPlot
        plot.clear()
        length = self.ui.lengthEdit.text().toInt()
        start = self.ui.startEdit.text().toDouble()
        end = self.ui.endEdit.text().toDouble()
                
        period = end[0] - start[0]
        inc = period / (length[0] - 1)
        
        self.time = []
        for i in range(length[0]):
            self.time.append(start[0] + inc * i)
        
        self.generateAmpData()
        self.plotData()  
    # generateEvenData()
    
    def generateRandomTimesData(self):
        print "random times data"
        plot = self.ui.qwtPlot
        plot.clear()
        length = self.ui.lengthEdit.text().toInt()
        start = self.ui.startEdit.text().toDouble()
        end = self.ui.endEdit.text().toDouble()
        
        self.time = []
        for i in range(length[0]):
            self.time.append(random.uniform(start[0],end[0]))
        self.time.sort()
        
        self.generateAmpData()
        self.plotData() 
    # generateRandomTimesData()
        
    def generateRandomDropsData(self):
        print "random data"
        pDrops = self.ui.percentDropsEdit.text().toDouble()
        plot = self.ui.qwtPlot
        plot.clear()
        length = self.ui.lengthEdit.text().toInt()
        start = self.ui.startEdit.text().toDouble()
        end = self.ui.endEdit.text().toDouble()
                
        period = end[0] - start[0]
        inc = period / (length[0] - 1)
        nDrops = int(round(pDrops[0]/100 * length[0]))
        
        self.time = []
        for i in range(length[0]):
            self.time.append(start[0] + inc * i)
        nKeep = length[0] - nDrops
        print "keeping:",nKeep,"points"
        self.time = random.sample(self.time,  nKeep)
        self.time.sort()

        
        self.generateAmpData()
               
        self.plotData()
    # generateRandomDropsData()
        
    def generateDutyCycleData(self):
        print "duty cycle data"
        plot = self.ui.qwtPlot
        plot.clear()
        pDuty = self.ui.dutyCycleEdit.text().toDouble()
        pulseLengthDuty = self.ui.dutyPulseLengthEdit.text().toInt();
        length = self.ui.lengthEdit.text().toInt()
        start = self.ui.startEdit.text().toDouble()
        end = self.ui.endEdit.text().toDouble()

        period = end[0] - start[0]
        inc = period / (length[0] - 1)         

        T = int((pulseLengthDuty[0] / (pDuty[0]/100)))
        
        
        print "pulse Length:",pulseLengthDuty[0]
        print "pulse:", pulseLengthDuty[0]*inc
        print "period Length (T):",T
        print "period:", T*inc
        
        keepStart = []
        nd = 0
        
        iPeriod = 0;      
        for i in range(length[0]):
            if (i%T == 0):
                for p in range(pulseLengthDuty[0]):
                    keepStart.append(i+p)
                    #print "keep",i+p,"period",iPeriod
                iPeriod += 1
            

              

        self.time = []
        for keep in keepStart:
            self.time.append(keep * inc)
        
        self.generateAmpData()
        self.plotData()

    # generateDutyCycleData() 
     
        
    def generateSpacingFileData(self):
        print "spacing file"
        
        plot = self.ui.qwtPlot
        plot.clear()
        pDuty = self.ui.dutyLengthEdit.text().toDouble()
        
        sFile = self.ui.spacingFileEdit.text()
        if sFile == "file":
            QtGui.QMessageBox.warning(self,self.tr("Select Spacing file"),self.tr("Please select a spacing file using the Get spacing file button!"))
            return
        
        file = QtCore.QFile(sFile)
        if not file.open(QtCore.QFile.ReadOnly | QtCore.QFile.Text):
            QtGui.QMessageBox.warning(self, self.tr("Spacing file"),
                                      self.tr("Cannot read file %1:\n%2.")
                                             .arg(fileName)
                                             .arg(file.errorString()))
            return
        
        
        data = QtCore.QTextStream()
        data.setDevice(file)
        data.setCodec("UTF-8")

        self.time = []
        while (data.atEnd() == False):
            self.time.append(data.readLine().toDouble()[0])
        
        self.generateAmpData()
        self.plotData()
        
    # generateSpacingFileData()
    
    def setNoise(self):        
        if (self.ui.addNoiseCheckBox.checkState() == QtCore.Qt.Checked):
            self.ui.noiseTotalPercentEdit.setEnabled(True)
            self.ui.noiseAmpPercentEdit.setEnabled(True)
            self.ui.noiseFreqPercentEdit.setEnabled(True)
            self.ui.noisePhasePercentEdit.setEnabled(True)
            self.ui.noisePercentLabel.setEnabled(True)
        else:
            self.ui.noiseTotalPercentEdit.setEnabled(False)
            self.ui.noiseAmpPercentEdit.setEnabled(False)
            self.ui.noiseFreqPercentEdit.setEnabled(False)
            self.ui.noisePhasePercentEdit.setEnabled(False)
            self.ui.noisePercentLabel.setEnabled(False)
    # setNoise()
        
    def getSpacingFile(self):
        fileName = QtGui.QFileDialog.getOpenFileName(self, 
                                                     self.tr("dataGen time spacing file"),
                                                     QtCore.QDir.currentPath(),
                                                     self.tr("spacing file (*.spacings *.times)"))
        if fileName.isEmpty():
            return
        self.ui.spacingFileEdit.setText(fileName)  
    # getSpacingFile()   

    def generateAmpData(self):
        print "generating amplitudes..."
        
        table = self.ui.componentsTable
        nComponents = table.rowCount()
        self.getComponents()
        
        pAmpNoise = 0
        pFreqNoise = 0
        pPhaseNoise = 0
        pTotalNoise = 0
        if (self.ui.addNoiseCheckBox.checkState() == QtCore.Qt.Checked):
            print "noise on!!"
            pAmpNoise = self.ui.noiseAmpPercentEdit.text().toDouble()[0]
            pFreqNoise = self.ui.noiseFreqPercentEdit.text().toDouble()[0]
            pPhaseNoise = self.ui.noisePhasePercentEdit.text().toDouble()[0]
            pTotalNoise = self.ui.noiseTotalPercentEdit.text().toDouble()[0]
        print "noise percent(total, amp,freq,phase):",pTotalNoise, pAmpNoise,pFreqNoise,pPhaseNoise
            
        self.amp = []

        for i in range(len(self.time)):
            a = 0.0
            for c in range(nComponents):
                cAmp = self.componentData[0][c]
                cFreq = self.componentData[1][c]
                cPhase = self.componentData[2][c]
                
                cTotalNoise = random.uniform(-1*pTotalNoise/100*cAmp,pTotalNoise/100*cAmp)
                cAmpNoise =  random.uniform(-1*pAmpNoise/100*cAmp,pAmpNoise/100*cAmp)
                cFreqNoise =  random.uniform(-1*pFreqNoise/100*cFreq,pFreqNoise/100*cFreq)
                cPhaseNoise =  random.uniform(-1*pPhaseNoise/100*math.pi*2,pPhaseNoise/100*math.pi*2)
                
                arg = (2.0*math.pi * self.time[i]*(cFreq + cFreqNoise)) +  (cPhase * math.pi) + cPhaseNoise                
                sin =  (cAmp + cAmpNoise)* math.cos(arg) + cTotalNoise
                a = a +sin
                if (  math.fabs(a) < 10e-12 ): # when very close to zero (numerical error), set to zero
                    a = 0
            self.amp.append(a)   
        
        
    def plotData(self):
        print "== plotting data"
        #print self.componentData
        #print self.time
        plot = self.ui.qwtPlot
        #plot.clear()
        self.curve1 = Qwt.QwtPlotCurve("Curve1")
        curve1 = self.curve1
        x = self.time
        y = self.amp
        curve1.setData(x,y)
        curve1.attach(plot)
        style = Qwt.QwtPlotCurve.NoCurve
        curve1.setStyle(style)
        curve1.setPen(Qt.QPen(Qt.Qt.blue))
        curve1.setSymbol(Qwt.QwtSymbol(Qwt.QwtSymbol.Ellipse,
                                       Qt.QBrush(Qt.Qt.gray),
                                       Qt.QPen(Qt.Qt.red),
                                       Qt.QSize(4,4)))
        
        grid = Qwt.QwtPlotGrid()
        grid.attach(plot)
        grid.setMajPen(Qt.QPen(Qt.Qt.black, 0, Qt.Qt.DotLine))
        plot.replot()
        
        # get ave,min,max time seperation
        seperation = [] 
        sumSep = 0.0
        #print "times",self.time
        for i in range(len(self.time)-1):
            seperation.append(self.time[i+1]-self.time[i])
            sumSep += seperation[i]
         
        #print sumSep
        aveSep = sumSep / (len(self.time)-1)
        #print aveSep
        self.ui.AveTimeSepLabel.setText(Qt.QString.number(aveSep))
        seperation.sort()
        #print "seps (",len(seperation),")",seperation
        #print "min sep:",seperation[0]
        #print "max sep:",seperation[len(seperation)-1]
        self.ui.MinTimeSepLabel.setText(Qt.QString.number(seperation[0]))
        self.ui.MaxTimeSepLabel.setText(Qt.QString.number(seperation[len(seperation)-1]))
        
    # plotData()
        
    def saveData(self):
        print "save data"
        # get the filename to save to
        fileName = QtGui.QFileDialog.getSaveFileName(self,self.tr("Save file"),
                                                     QtCore.QDir.currentPath(),
                                                     self.tr("signal gen files (*.*)"))
        
        # check the name exists 
        if fileName.isEmpty():
            return
        
        file = QtCore.QFile(fileName)
        if not file.open(QtCore.QFile.WriteOnly | QtCore.QFile.Text):
            QtGui.QMessageBox.warning(self, self.tr("sig gen data file"),
                                      self.tr("Cannot write file %1:\n%2.")
                                             .arg(fileName)
                                             .arg(file.errorString()))
            return
        
        self.out = QtCore.QTextStream()
        self.out.setDevice(file)
        self.out.setCodec("UTF-8")

        
        self.out.setRealNumberNotation(QtCore.QTextStream.FixedNotation)
        self.out.setRealNumberPrecision(12)
        for index, time in enumerate(self.time):
            self.out << time << "\t" << self.amp[index] << "\n"
                
        self.statusBar().showMessage(self.tr("Data file saved"), 2000)
        
    # saveData()
        
    def loadParameters(self):
        print "load parameters..."
        table = self.ui.componentsTable
        self.clearComponents()
        fileName = QtGui.QFileDialog.getOpenFileName(self, 
                                                     self.tr("dataGen parameter file"),
                                                     QtCore.QDir.currentPath(),
                                                     self.tr("parameter file (*.param)"))
        if fileName.isEmpty():
            return  
        
        
        file = QtCore.QFile(fileName)
        if not file.open(QtCore.QFile.ReadOnly | QtCore.QFile.Text):
            QtGui.QMessageBox.warning(self, self.tr("Parameter file"),
                                      self.tr("Cannot read file %1:\n%2.")
                                             .arg(fileName)
                                             .arg(file.errorString()))
            return
        
        pIn = QtCore.QTextStream()
        pIn.setDevice(file)
        pIn.setCodec("UTF-8")
        iParam = 0
        tableRow = 0
        while (pIn.atEnd() == False):  
            lineString = str(pIn.readLine().toAscii())
            value = lineString.partition('#')[0]
            value = value.strip()
            values = value.split('\t')
            print "(",iParam,")",values 
            if iParam == 0: # top comment line
                pass
            elif iParam == 1: # type of data
                self.ui.typeSelectBox.setCurrentIndex(int(values[0]))
            elif iParam == 2: # length of data
                self.ui.lengthEdit.setText(values[0])
            elif iParam == 3: # range
                self.ui.startEdit.setText(values[0])
                self.ui.endEdit.setText(values[1])
            elif iParam == 4: # noise
                self.ui.noiseTotalPercentEdit.setText(values[0])
                self.ui.noiseAmpPercentEdit.setText(values[1])
                self.ui.noiseFreqPercentEdit.setText(values[2])
                self.ui.noisePhasePercentEdit.setText(values[3])
            elif iParam == 5: # random drops
                self.ui.percentDropsEdit.setText(values[0])
            elif iParam == 6: # duty length 
                self.ui.dutyCycleEdit.setText(values[0])
                self.ui.dutyPulseLengthEdit.setText(values[1])
            elif iParam == 7: # spacings file
                self.ui.spacingFileEdit.setText(values[0])
            elif iParam == 8: # number of components
                pass
            else:
                amp = QtGui.QTableWidgetItem(values[0])
                freq = QtGui.QTableWidgetItem(values[1])
                phase = QtGui.QTableWidgetItem(values[2])
                self.ui.componentsTable.setRowCount(tableRow+1)
                table.setItem(tableRow,0,amp)
                table.setItem(tableRow,1,freq)
                table.setItem(tableRow,2,phase)
                table.resizeRowsToContents()
                tableRow += 1 
                
            iParam += 1
            
            
        
    # loadParameters()
        
    def saveParameters(self):
        print "save paramters" 
        # get the filename to save to
        fileName = QtGui.QFileDialog.getSaveFileName(self,self.tr("Save parameter file"),
                                                     QtCore.QDir.currentPath(),
                                                     self.tr("data gen paramter files (*.param)"))
        
        # check the name exists 
        if fileName.isEmpty():
            return
        
        file = QtCore.QFile(fileName)
        if not file.open(QtCore.QFile.WriteOnly | QtCore.QFile.Text):
            QtGui.QMessageBox.warning(self, self.tr("sig gen paramter file"),
                                      self.tr("Cannot write file %1:\n%2.")
                                             .arg(fileName)
                                             .arg(file.errorString()))
            return
        
        pfile = QtCore.QTextStream()
        pfile.setDevice(file)
        pfile.setCodec("UTF-8")
        
        pfile << "#dataGen parameter file\n"
        pfile << self.ui.typeSelectBox.currentIndex() << "\t#Data type: \t\"" << self.ui.typeSelectBox.currentText() << "\"\n"
        pfile << self.ui.lengthEdit.text() << "\t#Data Length (samples)\n"
        pfile << self.ui.startEdit.text() << "\t"<<  self.ui.endEdit.text() << "\t#Time range (start, end)\n"
        pfile << self.ui.noiseTotalPercentEdit.text() << "\t" <<self.ui.noiseAmpPercentEdit.text() << "\t"
        pfile << self.ui.noiseFreqPercentEdit.text() << "\t" << self.ui.noisePhasePercentEdit.text() << "\t#Noise %(total, amp, freq, phase)\n"
        pfile << self.ui.percentDropsEdit.text() << "\t#percent random drops\n"
        pfile << self.ui.dutyCycleEdit.text() << "\t" << self.ui.dutyPulseLengthEdit.text() << "\t#%Duty cycle, pulse length\n"
        pfile << self.ui.spacingFileEdit.text() << "\t#Spacings file\n"
        pfile << self.ui.componentsTable.rowCount()  << "\t#Number of components in components table (below)\n"
        self.getComponents()
        if self.ui.componentsTable.rowCount() > 0:      
            for index, amp in enumerate(self.componentData[0]):
                pfile << amp << "\t" << self.componentData[1][index] << "\t"\
                        << self.componentData[2][index] << "\t#Component " << index << " (amp, freq, phase)\n"
             
        self.statusBar().showMessage(self.tr("Paramter file saved"), 2000)
        
    # saveParamters()
    
    def addComponent(self):
        print "add"
        table = self.ui.componentsTable
        ampValue = QtGui.QTableWidgetItem("0.0")
        freqValue = QtGui.QTableWidgetItem("0.0")
        phaseValue = QtGui.QTableWidgetItem("0.0")
        row = table.rowCount()
        self.ui.componentsTable.setRowCount(row+1)
        table.setItem(row,0,ampValue)
        table.setItem(row,1,freqValue)
        table.setItem(row,2,phaseValue)
        table.setCurrentCell(row,table.currentColumn())
        self.ui.componentsTable.resizeRowsToContents()
    # addComponent()
        
        
    def getComponents(self):
        table = self.ui.componentsTable
        nComponents = table.rowCount()
        self.componentData = [[] for i in range(3)]
        for i in range(nComponents):
            amp = table.item(i,0).text().toDouble()
            freq = table.item(i,1).text().toDouble() 
            phase = table.item(i,2).text().toDouble()
            if (freq[1] == False or amp[1] == False or phase[1] == False):
                print "\nERROR: table value at row: ",i+1," has a non float value!"
                return
            self.componentData[0].append(amp[0])
            self.componentData[1].append(freq[0])
            self.componentData[2].append(phase[0])
    # getComponents()
        
    def deleteComponent(self):
        print "delete"
        table = self.ui.componentsTable
        currentRow = table.currentRow()
        print "r:",currentRow
        if currentRow != -1:
            table.removeRow(currentRow)
            if currentRow == 0:
                table.setCurrentCell(0,table.currentColumn())
            else:
                table.setCurrentCell(currentRow-1,table.currentColumn())
        else:
            row = table.rowCount()
            table.removeRow(row-1)
            table.setCurrentCell(row-1,table.currentColumn())
        self.ui.componentsTable.resizeRowsToContents()
        
    # removeComponent()
        
    def clearComponents(self):
        print "clear"
        table = self.ui.componentsTable
        row = table.rowCount()
        for i in range(1,row+1):
            table.removeRow(row-i)
        self.ui.componentsTable.resizeRowsToContents()
    # clearComponents()
        
    def comboBoxSelection(self,type):
        if  type == 0: # even
            self.ui.percentDropsEdit.setEnabled(False)
            self.ui.percentDropsLabel.setEnabled(False)
            self.ui.dutyCycleEdit.setEnabled(False)
            self.ui.dutyLengthLabel.setEnabled(False)
            self.ui.dutyPulseLengthEdit.setEnabled(False)
            self.ui.spacingFileButton.setEnabled(False)
            self.ui.spacingFileEdit.setEnabled(False)
        elif type == 1: #random times
            self.ui.percentDropsEdit.setEnabled(False)
            self.ui.percentDropsLabel.setEnabled(False)
            self.ui.dutyCycleEdit.setEnabled(False)
            self.ui.dutyLengthLabel.setEnabled(False)
            self.ui.dutyPulseLengthEdit.setEnabled(False)
            self.ui.spacingFileButton.setEnabled(False)
            self.ui.spacingFileEdit.setEnabled(False)
        elif type == 2: #random drops
            self.ui.percentDropsEdit.setEnabled(True)
            self.ui.percentDropsLabel.setEnabled(True)
            self.ui.dutyCycleEdit.setEnabled(False)
            self.ui.dutyLengthLabel.setEnabled(False)
            self.ui.dutyPulseLengthEdit.setEnabled(False)
            self.ui.spacingFileButton.setEnabled(False)
            self.ui.spacingFileEdit.setEnabled(False)     
        elif type == 3: #duty cycle
            self.ui.percentDropsEdit.setEnabled(False)
            self.ui.percentDropsLabel.setEnabled(False)
            self.ui.dutyCycleEdit.setEnabled(True)
            self.ui.dutyLengthLabel.setEnabled(True)
            self.ui.dutyPulseLengthEdit.setEnabled(True)
            self.ui.spacingFileButton.setEnabled(False)
            self.ui.spacingFileEdit.setEnabled(False)
        elif type == 4: #spacing file
            self.ui.percentDropsEdit.setEnabled(False)
            self.ui.percentDropsLabel.setEnabled(False)
            self.ui.dutyCycleEdit.setEnabled(False)
            self.ui.dutyLengthLabel.setEnabled(False)
            self.ui.dutyPulseLengthEdit.setEnabled(False)
            self.ui.spacingFileButton.setEnabled(True)
            self.ui.spacingFileEdit.setEnabled(True)
                        
                         
        
if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    myapp = Gui()
    myapp.show()
    sys.exit(app.exec_())
        
